package net.lanelife.casino.caipiao.service;

import java.util.HashMap;

import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceContext;

import net.lanelife.casino.caipiao.entity.BankRechargeOrder;
import net.lanelife.casino.caipiao.entity.CompanyBankCard;
import net.lanelife.casino.caipiao.entity.Job;
import net.lanelife.casino.caipiao.entity.Job.JobType;
import net.lanelife.casino.caipiao.entity.PaymentMerchant;
import net.lanelife.casino.caipiao.entity.PaymentRechargeOrder;
import net.lanelife.casino.caipiao.entity.RechargeRecord;
import net.lanelife.casino.caipiao.entity.TransferRecord;
import net.lanelife.casino.caipiao.entity.User;
import net.lanelife.casino.caipiao.entity.WithdrawRecord;
import net.lanelife.casino.caipiao.job.JobDispatcher;
import net.lanelife.casino.caipiao.job.info.RechargeJobInfo;
import net.lanelife.casino.caipiao.job.info.TransferAcceptJobInfo;
import net.lanelife.casino.caipiao.job.info.TransferApplyJobInfo;
import net.lanelife.casino.caipiao.job.info.TransferCancelJobInfo;
import net.lanelife.casino.caipiao.job.info.TransferRejectJobInfo;
import net.lanelife.casino.caipiao.job.info.WithdrawAcceptJobInfo;
import net.lanelife.casino.caipiao.job.info.WithdrawApplyJobInfo;
import net.lanelife.casino.caipiao.job.info.WithdrawRejectJobInfo;
import net.lanelife.casino.caipiao.repository.api.ICompanyBankCardRepository;
import net.lanelife.casino.caipiao.repository.api.IPaymentMerchantRepository;
import net.lanelife.casino.caipiao.repository.api.IRechargeRecordRepository;
import net.lanelife.casino.caipiao.repository.api.IUserRepository;
import net.lanelife.casino.caipiao.service.api.ITransactionService;
import net.lanelife.casino.caipiao.service.api.IUserDailyStatisticsService;

import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 交易服务
 * 原则：
 * 		UTIME: 交易明细与报表应以用户余额变更的时间点为基准
 *      BTIME: 银行交易明细与银行报表应以银行卡余额变更的时间点为基准
 * @author Lane
 *
 */
@Service
public class TransactionService implements ITransactionService {
	
	@PersistenceContext
	private EntityManager em;
	
	@Autowired
	private JobDispatcher jobDispatcher;
	
	@Autowired
	private IUserRepository userRepository;
	
	@Autowired
	private ICompanyBankCardRepository companyBankCardRepository;
	
	@Autowired
	private IPaymentMerchantRepository paymentMerchantRepository;
	
	@Autowired
	private IRechargeRecordRepository rechargeRecordRepository;
	
	@Autowired
	private IUserDailyStatisticsService userDailyStatisticsService;

	
	@Transactional(rollbackFor = Exception.class)
	private void recharge(Integer userId, Double amount, Double fee, RechargeRecord.Way way, String payerBankName, String payerAccountName, String payerAccountNo, String payeeBankName, String payeeAccountName, String payeeAccountNo, Double bankCurrentBalance) throws Exception {
		
		final RechargeJobInfo jobInfo = new RechargeJobInfo();                                          // 任务信息
		final Double       realAmount = amount + fee;                                                   // 实际充值金额 ＝ 充值金额 + 手续费
		final User               user = userRepository.findOne(userId, LockModeType.PESSIMISTIC_WRITE); // 充值用户
		
		// 更新用户
		jobInfo.setUPB(user.getAccountBalance());
		user.setAccountBalance(user.getAccountBalance() + realAmount);
		user.setRechargeTimes(user.getRechargeTimes() + 1);
		user.setRechargeTotal(user.getRechargeTotal() + realAmount);
		user.setRechargeMax(user.getRechargeMax() > realAmount ? user.getRechargeMax() : realAmount);
		
		// 保存充值记录
		RechargeRecord record = new RechargeRecord(user);
		record.setPayerBankName(payerBankName);
		record.setPayerAccountName(payerAccountName);
		record.setPayerAccountNo(payerAccountNo);
		record.setPayeeBankName(payeeBankName);
		record.setPayeeAccountName(payeeAccountName);
		record.setPayeeAccountNo(payeeAccountNo);
		record.setWay(way);
		record.setAmount(amount);
		record.setFee(fee);
		rechargeRecordRepository.save(record);
		
		// 派发充值任务
		jobInfo.setRID(record.getId());
		jobInfo.setUCB(user.getAccountBalance());
		jobInfo.setBCB(bankCurrentBalance);
		jobInfo.setTIME(record.getCreatedDate().getMillis());
		jobDispatcher.dispatch(new Job(JobType.RECHARGE), jobInfo);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void recharge(BankRechargeOrder order) throws Exception {
		// 更新公司收款卡
		CompanyBankCard card = companyBankCardRepository.findByAccountNo(order.getPayeeAccountNo()); //查询指定卡号的收款卡
		em.refresh(card, LockModeType.PESSIMISTIC_WRITE); // 锁定这张收款卡
		card.setCurrentBalance(card.getCurrentBalance() + order.getAmount());
		// 调用充值方法
		recharge(order.getUserId(), order.getAmount(), order.getFee(), RechargeRecord.Way.BT, order.getPayerBankName(), order.getPayerAccountName(), order.getPayerAccountNo(), order.getPayeeBankName(), order.getPayeeAccountName(), order.getPayeeAccountNo(), card.getCurrentBalance());
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void recharge(PaymentRechargeOrder order) throws Exception {
		// 更新支付商户
		PaymentMerchant merchant = paymentMerchantRepository.findByMerchantNo(order.getMerchantNo()); //查询指定商户号
		em.refresh(merchant, LockModeType.PESSIMISTIC_WRITE); // 锁定这个商户
		merchant.setCurrentBalance(merchant.getCurrentBalance() + order.getFactAmount());
		// 调用充值方法，在线支付无法知道付款方户名和卡号
		recharge(order.getUserId(), order.getFactAmount(), order.getFee(), RechargeRecord.Way.OP, order.getPayerBankName(), "", "", order.getPlatformName(), order.getMerchantName(), order.getMerchantNo(), merchant.getCurrentBalance());
	}

	@SuppressWarnings("serial")
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void withdrawApply(WithdrawRecord record) throws Exception {
		
		final WithdrawApplyJobInfo jobInfo = new WithdrawApplyJobInfo();                                                 // 任务信息
		final LocalDate         reportDate = new LocalDate(record.getCreatedDate().getMillis());                         // 报表日期
		final Double                amount = record.getAmount();                                                         // 提款金额
		final User                    user = userRepository.findOne(record.getUserId(), LockModeType.PESSIMISTIC_WRITE); // 提款用户
		
		// 更新用户
		jobInfo.setUPB(user.getAccountBalance());
		user.setAccountBalance(user.getAccountBalance() - amount);
		user.setLastWithdrawTime(record.getCreatedDate());
		
		// 更新用户日结表，因需判断每日提款，所以不送往后台任务
		userDailyStatisticsService.update(user, reportDate, new HashMap<String, Object>(){{
			put("WITHDRAW_TIMES_APPLY", 1); 
			put("WITHDRAW_TOTAL_APPLY", amount);
		}});
		
		// 派发提款申请任务
		jobInfo.setRID(record.getId());
		jobInfo.setUCB(user.getAccountBalance());
		jobInfo.setTIME(record.getCreatedDate().getMillis());
		jobDispatcher.dispatch(new Job(JobType.WITHDRAW_APPLY), jobInfo);
	}

	@SuppressWarnings("serial")
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void withdrawAccept(final WithdrawRecord record) throws Exception {  
		
		final WithdrawAcceptJobInfo jobInfo = new WithdrawAcceptJobInfo();                                                // 任务信息
		final LocalDate          reportDate = new LocalDate(record.getCreatedDate().getMillis());                         // 报表日期
		final Double                 amount = record.getAmount();                                                         // 提款金额
		final Double                    fee = record.getFee();                                                            // 平台手续费
		final Double                bankFee = record.getBankFee();                                                        // 银行手续费
		final Double             realAmount = amount - fee + bankFee;                                                     // 实际出款金额 ＝ 提款金额 - 平台手续费 + 银行手续费
		final User                     user = userRepository.findOne(record.getUserId(), LockModeType.PESSIMISTIC_WRITE); // 提款用户
		final CompanyBankCard          card = companyBankCardRepository.findByAccountNo(record.getPayerAccountNo());      // 出款卡
		
		// 更新用户
		user.setWithdrawTimes(user.getWithdrawTimes() +1);
		user.setWithdrawTotal(user.getWithdrawTotal() + amount);
		user.setWithdrawMax(user.getWithdrawMax() > amount ? user.getWithdrawMax() : amount);
		
		// 更新公司出款卡
		em.refresh(card, LockModeType.PESSIMISTIC_WRITE);
		card.setCurrentBalance(card.getCurrentBalance() - realAmount);
		
		// 更新用户日结表，因需判断每日提款，所以不送往后台任务
		userDailyStatisticsService.update(user, reportDate, new HashMap<String, Object>(){{
			put("WITHDRAW_TIMES_ACCEPT", 1); 
			put("WITHDRAW_TOTAL_ACCEPT", amount); 
			put("WITHDRAW_FEE_ACCEPT", fee); 
			put("WITHDRAW_BANK_FEE_ACCEPT", bankFee);
		}});
		
		// 派发提款通过任务
		jobInfo.setRID(record.getId());
		jobInfo.setBCB(card.getCurrentBalance());
		jobInfo.setUTIME(record.getCreatedDate().getMillis());
		jobInfo.setBTIME(System.currentTimeMillis());
		jobDispatcher.dispatch(new Job(JobType.WITHDRAW_ACCEPT), jobInfo);
	}

	@SuppressWarnings("serial")
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void withdrawReject(WithdrawRecord record) throws Exception {
		
		final WithdrawRejectJobInfo jobInfo = new WithdrawRejectJobInfo();                                                // 任务信息
		final DateTime          currentTime = DateTime.now();                                                             // 操作时间
		final LocalDate          reportDate = new LocalDate(currentTime.getMillis());                                     // 报表日期
		final Double                 amount = record.getAmount();                                                         // 提款金额
		final User                     user = userRepository.findOne(record.getUserId(), LockModeType.PESSIMISTIC_WRITE); // 提款用户
		
		// 更新用户
		jobInfo.setUPB(user.getAccountBalance());
		user.setAccountBalance(user.getAccountBalance() + amount);
		
		// 更新用户日结表，因需判断每日提款，所以不送往后台任务
		userDailyStatisticsService.update(user, reportDate, new HashMap<String, Object>(){{
			put("WITHDRAW_TIMES_REJECT", 1); 
			put("WITHDRAW_TOTAL_REJECT", amount);
		}});
		
		// 派发提款拒绝任务
		jobInfo.setRID(record.getId());
		jobInfo.setUCB(user.getAccountBalance());
		//jobDispatcher.dispatch(new Job(JobType.WITHDRAW_REJECT, currentTime), jobInfo);
		
	}

	@SuppressWarnings("serial")
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void transferApply(TransferRecord record) throws Exception {
		final TransferApplyJobInfo jobInfo = new TransferApplyJobInfo();                                                      // 任务信息
		final LocalDate         reportDate = new LocalDate(record.getCreatedDate().getMillis());                              // 报表日期
		final Double                amount = record.getAmount();                                                              // 转账金额
		final User                   payer = userRepository.findOne(record.getPayerUserId(), LockModeType.PESSIMISTIC_WRITE); // 转出方
		
		// 更新用户
		jobInfo.setUPB(payer.getAccountBalance());
		payer.setAccountBalance(payer.getAccountBalance() - amount);
		
		// 更新用户日结表
		userDailyStatisticsService.update(payer, reportDate, new HashMap<String, Object>(){{
			put("TRANSFER_TIMES_APPLY", 1); 
			put("TRANSFER_TOTAL_APPLY", amount);
		}});
		
		// 派发转账申请任务
		jobInfo.setRID(record.getId());
		jobInfo.setUCB(payer.getAccountBalance());
		//jobDispatcher.dispatch(new Job(JobType.TRANSFER_APPLY, record.getCreatedDate()), jobInfo);
	}

	@SuppressWarnings("serial")
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void transferAccept(TransferRecord record) throws Exception {
		
		final TransferAcceptJobInfo jobInfo = new TransferAcceptJobInfo();                                                     // 任务信息
		final LocalDate          reportDate = new LocalDate(record.getCreatedDate().getMillis());                              // 报表日期
		final Double                 amount = record.getAmount();                                                              // 转账金额
		final Double                    fee = record.getFee();                                                                 // 平台手续费
		final Double             realAmount = amount - fee;                                                                    // 实际转入金额 ＝ 转账金额 - 平台手续费
		final User                    payer = userRepository.findOne(record.getPayerUserId(), LockModeType.PESSIMISTIC_WRITE); // 转出方
		final User                    payee = userRepository.findOne(record.getPayeeUserId(), LockModeType.PESSIMISTIC_WRITE); // 转入方
		
		// 更新用户
		payer.setTransferOutTimes(payer.getTransferOutTimes() + 1);
		payer.setTransferOutTotal(payer.getTransferOutTotal() + amount);
		payer.setTransferOutMax(payer.getTransferOutMax() > amount ? payer.getTransferOutMax() : amount);
		jobInfo.setUPB(payee.getAccountBalance());
		payee.setAccountBalance(payee.getAccountBalance() + realAmount);
		payee.setTransferInTimes(payee.getTransferInTimes() + 1);
		payee.setTransferInTotal(payee.getTransferInTotal() + amount);
		payee.setTransferInMax(payee.getTransferInMax() > amount ? payee.getTransferInMax() : amount);
		
		// 更新用户日结表
		userDailyStatisticsService.update(payer, reportDate, new HashMap<String, Object>(){{
			put("TRANSFER_TIMES_RO", 1); 
			put("TRANSFER_TOTAL_RO", amount); 
			put("TRANSFER_FEE_RO", fee);
		}});
		userDailyStatisticsService.update(payee, reportDate, new HashMap<String, Object>(){{
			put("TRANSFER_TIMES_RI", 1); 
			put("TRANSFER_TOTAL_RI", amount); 
			put("TRANSFER_FEE_RI", fee); 
		}});
		
		// 派发转账通过任务
		jobInfo.setRID(record.getId());
		jobInfo.setUCB(payee.getAccountBalance());
		//jobDispatcher.dispatch(new Job(JobType.TRANSFER_ACCEPT, record.getCreatedDate()), jobInfo);
	}

	@SuppressWarnings("serial")
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void transferReject(TransferRecord record) throws Exception {
		
		final TransferRejectJobInfo jobInfo = new TransferRejectJobInfo();                                                     // 任务信息
		final LocalDate          reportDate = new LocalDate(record.getCreatedDate().getMillis());                              // 报表日期
		final Double                 amount = record.getAmount();                                                              // 转账金额
		final User                    payer = userRepository.findOne(record.getPayerUserId(), LockModeType.PESSIMISTIC_WRITE); // 转出方
		
		// 更新用户
		jobInfo.setUPB(payer.getAccountBalance());
		payer.setAccountBalance(payer.getAccountBalance() + amount);
		
		// 更新用户日结表
		userDailyStatisticsService.update(payer, reportDate, new HashMap<String, Object>(){{
			put("TRANSFER_TIMES_REJECT", 1); 
			put("TRANSFER_TOTAL_REJECT", amount);
		}});
		
		// 派发转账拒绝任务
		jobInfo.setRID(record.getId());
		jobInfo.setUCB(payer.getAccountBalance());
		//jobDispatcher.dispatch(new Job(JobType.TRANSFER_REJECT, record.getCreatedDate()), jobInfo);
	}

	@SuppressWarnings("serial")
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void transferCancel(TransferRecord record) throws Exception {
		
		final TransferCancelJobInfo jobInfo = new TransferCancelJobInfo();                                                     // 任务信息
		final LocalDate          reportDate = new LocalDate(record.getCreatedDate().getMillis());                              // 报表日期
		final Double                 amount = record.getAmount();                                                              // 转账金额
		final Double                    fee = record.getFee();                                                                 // 平台手续费
		final Double             realAmount = amount - fee;                                                                    // 实际转入金额 ＝ 转账金额 - 平台手续费
		final User                    payer = userRepository.findOne(record.getPayerUserId(), LockModeType.PESSIMISTIC_WRITE); // 转出方
		final User                    payee = userRepository.findOne(record.getPayeeUserId(), LockModeType.PESSIMISTIC_WRITE); // 转入方
		
		// 更新用户，此时已无法计算最大转账金额
		jobInfo.setOUPB(payer.getAccountBalance());
		payer.setAccountBalance(payer.getAccountBalance() + amount);
		payer.setTransferOutTimes(payer.getTransferOutTimes() - 1);
		payer.setTransferOutTotal(payer.getTransferOutTotal() - amount);
		jobInfo.setIUPB(payee.getAccountBalance());
		payee.setAccountBalance(payee.getAccountBalance() - realAmount);
		payee.setTransferInTimes(payee.getTransferInTimes() - 1);
		payee.setTransferInTotal(payee.getTransferInTotal() - amount);
		
		// 更新用户日结表
		userDailyStatisticsService.update(payer, reportDate, new HashMap<String, Object>(){{
			put("TRANSFER_TIMES_ROC", 1); 
			put("TRANSFER_TOTAL_ROC", amount); 
			put("TRANSFER_FEE_ROC", fee);
		}});
		userDailyStatisticsService.update(payee, reportDate, new HashMap<String, Object>(){{
			put("TRANSFER_TIMES_RIC", 1); 
			put("TRANSFER_TOTAL_RIC", amount); 
			put("TRANSFER_FEE_RIC", fee); 
		}});
		
		// 派发转账撤销任务
		jobInfo.setRID(record.getId());
		jobInfo.setOUCB(payer.getAccountBalance());
		jobInfo.setIUCB(payee.getAccountBalance());
		//jobDispatcher.dispatch(new Job(JobType.TRANSFER_CANCEL, record.getCreatedDate()), jobInfo);
		
	}

}
